package edu.pdx.cs.cs542s07.minichess;

import java.util.ArrayList;
import java.util.Date;

/**
 * Handles the Negamax functionality of the game.
 * @author Michael C Smith <maxomai at gmail dot com>
 *
 */
public class NegamaxFactory {
	
	static final int NEGAMAX_DEPTH = 4;
	static final int MAXIMUM_TIME = 1000;
	private MoveFactory movefac;
	private ScoreFactory scorefac;
	
	NegamaxFactory() {
		 this.movefac = new MoveFactory();
		 this.scorefac = new ScoreFactory();
	}
	
	/**
	 * Iterates through the depths until it comes up with a good solution. TODO
	 * @param b
	 * @return
	 */
	Move negamax(Board board) {
		
		Date startTime = new Date();
		Move bestMove = Move.RESIGN;
		int scoreZero = ScoreFactory.OPPONENT_WINS;
		Board b = board.copy();
		ArrayList<Move> moves = movefac.generateScoredMoves(b);
		for (int depth = NEGAMAX_DEPTH; true; depth++) {
			int alphaZero = ScoreFactory.OPPONENT_WINS;
			for (Move move:moves) {

				try {
					b.move(move);
				} catch (IllegalMoveException e) {
					e.printStackTrace();
					System.out.println("Unrecoverable error. Exiting.");
					System.out.println(b.toString());
					MoveFactoryTest.printMoves(moves);
					System.exit(0);
				}
				
				int negamaxScore;
				try {
					negamaxScore = negamaxHelper(b, depth, ScoreFactory.OPPONENT_WINS, -alphaZero, startTime);
				} catch (OutOfTimeException e) {
					return bestMove;
				}
				
				alphaZero = Math.max(alphaZero, -negamaxScore);
				if (alphaZero > scoreZero) {
					bestMove = move;
					scoreZero = alphaZero;
				}
				
				b.undo();
			}
		}
	}
	
	class OutOfTimeException extends Exception {
	}
	
//	Move negamax(Board b) {
//		
//		int depth = NEGAMAX_DEPTH;
//		Move bestMove = Move.RESIGN;
//		int scoreZero = ScoreFactory.OPPONENT_WINS;
//		int alphaZero = ScoreFactory.OPPONENT_WINS;
//		ArrayList<Move> moves = movefac.generateMoves(b);
//		for (Move move:moves) {
//			//Board newBoard;
//			try {
//				//newBoard = b.simulatesMove(move);
//				b.move(move);
//			} catch (IllegalMoveException e) {
//				e.printStackTrace();
//				System.out.println("Unrecoverable error. Exiting.");
//				System.out.println(b.toString());
//				MoveFactoryTest.printMoves(moves);
//				System.exit(0);
//				//newBoard = new Board(); // never executes, but makes compiler happy.
//			}
//			
//			int negamaxScore = negamaxHelper(b, depth, ScoreFactory.OPPONENT_WINS, -alphaZero);
//			int score = Math.max(scoreZero, -negamaxScore);
//			alphaZero = Math.max(alphaZero, score);
//			if (score > scoreZero) {
//				bestMove = move;
//				scoreZero = score;
//			}
//			b.undo();
//		}
//		return bestMove;
//	}
	
	int negamaxHelper(Board b, int depth, int alphaZero, int beta, Date start) 
	throws OutOfTimeException {
		Date now = new Date();
		if (now.getTime() - start.getTime() > MAXIMUM_TIME) {
			throw new OutOfTimeException();
		}
		
		if (b.isGameOver() || depth == 0) 
			return b.getScore();
		
		int score = ScoreFactory.OPPONENT_WINS;
		int alpha = alphaZero;
		ArrayList<Move> moves = movefac.generateScoredMoves(b);
		for (Move move:moves) {			
			try {
				//newBoard = b.simulatesMove(move);
				b.move(move);
			} catch (IllegalMoveException e) {
				e.printStackTrace();
				System.out.println("Unrecoverable error. Exiting.");
				System.out.println(b.toString());
				MoveFactoryTest.printMoves(moves);
				System.exit(0);
				//board = new Board(); // never executes, but makes compiler happy.
			}
			
			int negamaxScore = negamaxHelper(b, depth-1, -beta, -alpha, start);
			score = Math.max(score, -negamaxScore);
			alpha = Math.max(alpha, score);
			b.undo();
			if (score >= beta) 
				return score;		
		}
		
		return score;
	}
	

	


}
